using Rogan.Utility.Common;
using Rogan.Utility.Extensions;
using System;
using System.Collections;
using System.Data;


namespace Rogan.Data
{
    [Serializable]
    public class Field : ICloneable<Field>
    {
        private readonly string _fieldName;
        private readonly string _tableName;
        private readonly string _aliasName;
        private readonly string _description;
        private readonly DbType? _parameterDbType;
        private readonly int? _parameterSize;
        public static readonly Field All = new Field("*");
        public DbType? ParameterDbType
        {
            get
            {
                return _parameterDbType;
            }
        }
        public int? ParameterSize
        {
            get
            {
                return _parameterSize;
            }
        }
        public string Name
        {
            get
            {
                if (_aliasName.IsNullOrEmpty())
                {
                    return _fieldName;
                }
                return _aliasName;
            }
        }
        public string PropertyName
        {
            get
            {
                return _fieldName;
            }
        }
        public string AliasName
        {
            get
            {
                return _aliasName;
            }
        }
        public string Description
        {
            get
            {
                return _description;
            }
        }
        public string TableName
        {
            get
            {
                if (_tableName.IsNullOrEmpty())
                {
                    return _tableName;
                }
                return "{0}" + _tableName + "{1}";
            }
        }
        public string FieldName
        {
            get
            {
                if (EntityHelper.IsCompletedFild(_fieldName))
                {
                    return _fieldName;
                }
                return "{0}".Add(_fieldName).Add("{1}");
            }
        }
        public string FieldNameWithTable
        {
            get
            {
                if (string.IsNullOrEmpty(_tableName))
                {
                    return FieldName;
                }
                return TableName + "." + FieldName;
            }
        }
        public string FullName
        {
            get
            {
                if (string.IsNullOrEmpty(_aliasName))
                {
                    return FieldNameWithTable;
                }
                return FieldNameWithTable + " AS {0}" + _aliasName + "{1}";
            }
        }
        public OrderByExpression Desc
        {
            get
            {
                return new OrderByExpression(this, OrderByDirection.Desc);
            }
        }
        public OrderByExpression Asc
        {
            get
            {
                return new OrderByExpression(this, OrderByDirection.Asc);
            }
        }
        public GroupByExpression GroupBy
        {
            get
            {
                return new GroupByExpression(this);
            }
        }
        private Field()
        {
        }
        public Field(string fieldName)
            : this(fieldName, null)
        {
        }
        public Field(string fieldName, string tableName)
            : this(fieldName, tableName, null, null, fieldName)
        {
        }
        public Field(string fieldName, string tableName, DbType? dbType)
            : this(fieldName, tableName, dbType, null, null)
        {
        }
        public Field(string fieldName, string tableName, DbType? dbType, string description)
            : this(fieldName, tableName, dbType, null, description)
        {
        }
        public Field(string fieldName, string tableName, string description)
            : this(fieldName, tableName, null, null, description)
        {
        }
        public Field(string fieldName, string tableName, DbType? parameterDbType, int? parameterSize, string description)
            : this(fieldName, tableName, parameterDbType, parameterSize, description, null)
        {
        }
        public Field(string fieldName, string tableName, DbType? parameterDbType, int? parameterSize, string description, string aliasName)
        {
            _fieldName = fieldName;
            _tableName = tableName;
            _description = description;
            _aliasName = aliasName;
            _parameterDbType = parameterDbType;
            _parameterSize = parameterSize;
        }
        public bool Equals(Field field)
        {
            return (!Equals(field, null)) && FullName.Equals(field.FullName);
        }
        public static bool IsNullOrEmpty(Field field)
        {
            return Equals(field, null) || string.IsNullOrEmpty(field.PropertyName);
        }
        public static Field Create(Field leftField, Field rightField, QueryOperator op)
        {
            if (IsNullOrEmpty(leftField) && IsNullOrEmpty(rightField))
            {
                return null;
            }
            if (IsNullOrEmpty(leftField))
            {
                return rightField;
            }
            if (IsNullOrEmpty(rightField))
            {
                return leftField;
            }
            return new Field(string.Concat(new[]
			{
				"(",
				leftField.FieldNameWithTable,
				DataHelper.GetOperatorQueryText(op),
				rightField.FieldNameWithTable,
				")"
			}));
        }
        public Field SetTableName(string tableName)
        {
            return new Field(_fieldName, tableName, _parameterDbType, _parameterSize, _description, _aliasName);
        }
        public Field As(string aliasName)
        {
            return new Field(_fieldName, _tableName, _parameterDbType, _parameterSize, _description, aliasName);
        }
        public Field As(Field field)
        {
            return As(field.Name);
        }
        public Field At(string tableName)
        {
            return new Field(_fieldName, tableName, _fieldName);
        }
        public Field At(Entity table)
        {
            if (table == null)
            {
                return this;
            }
            return new Field(PropertyName, table.GetName(), _fieldName);
        }
        public Field IsNull(Field field)
        {
            return new Field(string.Concat(new[]
			{
				"isnull(",
				 FieldNameWithTable,
				",",
				field.FieldNameWithTable,
				")"
			})).As(this);
        }
        public Field IsNull(object value)
        {
            return new Field(string.Concat(new[]
			{
				"isnull(",
				 FieldNameWithTable,
				",",
				DataHelper.FormatValue(value),
				")"
			})).As(this);
        }
        public Field Count()
        {
            if (PropertyName.Trim().Equals("*"))
            {
                return new Field("count(*)").As("cnt");
            }
            return new Field("count(" + FieldNameWithTable + ")").As(this);
        }
        public Field Sum()
        {
            return new Field("sum(" + FieldNameWithTable + ")").As(this);
        }
        public Field Avg()
        {
            return new Field("avg(" + FieldNameWithTable + ")").As(this);
        }
        public Field Len()
        {
            return new Field("len(" + FieldNameWithTable + ")").As(this);
        }
        public Field Trim()
        {
            return new Field("ltrim(rtrim(" + FieldNameWithTable + "))").As(this);
        }
        public Field Max()
        {
            return new Field("max(" + FieldNameWithTable + ")").As(this);
        }
        public Field Min()
        {
            return new Field("min(" + FieldNameWithTable + ")").As(this);
        }
        public Field Left(int length)
        {
            return new Field(string.Concat(new[]
			{
				"left(",
				 FieldNameWithTable,
				",",
				length.ToString(),
				")"
			})).As(this);
        }
        public Field Right(int length)
        {
            return new Field(string.Concat(new[]
			{
				"right(",
				 FieldNameWithTable,
				",",
				length.ToString(),
				")"
			})).As(this);
        }
        public Field Substring(int startIndex, int endIndex)
        {
            return new Field(string.Concat(new[]
			{
				"substring(",
				 FieldNameWithTable,
				",",
				startIndex.ToString(),
				",",
				endIndex.ToString(),
				")"
			})).As(this);
        }
        public Field Replace(string oldValue, string newValue)
        {
            return new Field(string.Concat(new[]
			{
				"replace(",
				 FieldNameWithTable,
				",",
				DataHelper.FormatValue(oldValue),
				",",
				DataHelper.FormatValue(newValue),
				")"
			})).As(this);
        }
        public Field Year()
        {
            return new Field("datepart(year," + FieldNameWithTable + ")").As(this);
        }
        public Field Month()
        {
            return new Field("datepart(month," + FieldNameWithTable + ")").As(this);
        }
        public Field Day()
        {
            return new Field("datepart(day," + FieldNameWithTable + ")").As(this);
        }
        public WhereExpression SelectIn(IEnumerable values)
        {
            return WhereExpression.Create(this, values, QueryOperator.In);
        }
        public WhereExpression SelectNotIn(IEnumerable values)
        {
            return WhereExpression.Create(this, values, QueryOperator.NotIn);
        }
        public WhereExpression SelectIn(IEntityQueryable subQuery)
        {
            return WhereExpression.Create(this, subQuery, QueryOperator.In);
        }
        public WhereExpression SelectNotIn(IEntityQueryable subQuery)
        {
            return WhereExpression.Create(this, subQuery, QueryOperator.NotIn);
        }
        public WhereExpression Between(object leftValue, object rightValue)
        {
            return this >= leftValue && this <= rightValue;
        }
        public WhereExpression Like(string value)
        {
            return WhereExpression.Create(this, "%" + value + "%", QueryOperator.Like);
        }
        public WhereExpression StartsWith(string value)
        {
            return WhereExpression.Create(this, "%" + value, QueryOperator.Like);
        }
        public WhereExpression EndsWith(string value)
        {
            return WhereExpression.Create(this, value + "%", QueryOperator.Like);
        }

        public Field Clone()
        {
            return DataHelper.Clone(this) as Field;
        }
        public static WhereExpression operator ==(Field leftField, Field rightField)
        {
            if (Equals(rightField, null))
            {
                return WhereExpression.Create(leftField, null, QueryOperator.Equal);
            }
            return WhereExpression.Create(leftField, rightField, QueryOperator.Equal);
        }
        public static WhereExpression operator !=(Field leftField, Field rightField)
        {
            if (rightField == null)
            {
                return WhereExpression.Create(leftField, null, QueryOperator.NotEqual);
            }
            return WhereExpression.Create(leftField, rightField, QueryOperator.NotEqual);
        }
        public static WhereExpression operator >(Field leftField, Field rightField)
        {
            return WhereExpression.Create(leftField, rightField, QueryOperator.Greater);
        }
        public static WhereExpression operator >=(Field leftField, Field rightField)
        {
            return WhereExpression.Create(leftField, rightField, QueryOperator.GreaterOrEqual);
        }
        public static WhereExpression operator <(Field leftField, Field rightField)
        {
            return WhereExpression.Create(leftField, rightField, QueryOperator.Less);
        }
        public static WhereExpression operator <=(Field leftField, Field rightField)
        {
            return WhereExpression.Create(leftField, rightField, QueryOperator.LessOrEqual);
        }
        public static WhereExpression operator ==(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.Equal);
        }
        public static WhereExpression operator ==(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.Equal);
        }
        public static WhereExpression operator !=(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.NotEqual);
        }
        public static WhereExpression operator !=(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.NotEqual);
        }
        public static WhereExpression operator >(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.Greater);
        }
        public static WhereExpression operator >(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.Less);
        }
        public static WhereExpression operator >=(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.GreaterOrEqual);
        }
        public static WhereExpression operator >=(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.LessOrEqual);
        }
        public static WhereExpression operator <(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.Less);
        }
        public static WhereExpression operator <(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.Greater);
        }
        public static WhereExpression operator <=(Field field, object value)
        {
            return WhereExpression.Create(field, value, QueryOperator.LessOrEqual);
        }
        public static WhereExpression operator <=(object value, Field field)
        {
            return WhereExpression.Create(value, field, QueryOperator.GreaterOrEqual);
        }
        public static Field operator +(Field leftField, Field rightField)
        {
            return Create(leftField, rightField, QueryOperator.Add);
        }
        public static Field operator -(Field leftField, Field rightField)
        {
            return Create(leftField, rightField, QueryOperator.Subtract);
        }
        public static Field operator *(Field leftField, Field rightField)
        {
            return Create(leftField, rightField, QueryOperator.Multiply);
        }
        public static Field operator /(Field leftField, Field rightField)
        {
            return Create(leftField, rightField, QueryOperator.Divide);
        }
        public static Field operator %(Field leftField, Field rightField)
        {
            return Create(leftField, rightField, QueryOperator.Modulo);
        }
    }
    [Serializable]
    public class Field<T> : Field where T : Entity
    {
        public Field(string fileName)
            : base(fileName, EntityHelper.GetName<T>())
        {
        }
    }
}
